package com.zjxf.realm;

import com.zjxf.base.bean.po.Admin;
import com.zjxf.base.bean.po.Seller;
import com.zjxf.base.bean.po.User;
import com.zjxf.base.common.SysConst;
import com.zjxf.base.common.SystemStatusCode;
import com.zjxf.cache.CacheKeys;
import com.zjxf.properties.AccountProperties;
import com.zjxf.redis.StringRedisService;
import com.zjxf.service.AdminService;
import com.zjxf.service.SellerService;
import com.zjxf.token.MyUserNamePasswordToken;
import com.zjxf.utils.UserUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * created with IntelliJ IDEA
 *
 * @author: create by limu
 * Date: 2021/3/15
 * Time：11:18
 */
@Slf4j
public class MyShiroRealm extends AuthorizingRealm {

    @Autowired
    private SellerService sellerService;

    @Autowired
    private AdminService adminService;

    @Setter
    private StringRedisService stringRedisService;

    @Setter
    private AccountProperties accountProperties;


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("授权中---授权信息 {}", principalCollection.toString());
        User user = (User) principalCollection.getPrimaryPrincipal();
//        Role role = roleService.findById(user.getRoleId()).orElseThrow(() -> new AccountException("该用户角色不存在或者已经被删除"));
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        info.addStringPermissions(Arrays.asList(role.getPermission().split(SysConst.SPLIT_TAG)));
//        Set<String> strings = new HashSet<>();
//        strings.add(role.getName());
//        info.setRoles(strings);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        MyUserNamePasswordToken token = (MyUserNamePasswordToken) authenticationToken;
        if (token.getLoginType().equals(SysConst.loginType.ADMIN.getCode())) {
            Admin dbAdmin = adminService.findByUsernameAndDeleteState(token.getUsername(), SysConst.DeleteState.UN_DELETE_STATE.getCode()).orElseThrow(() -> new AccountException("数据不存在或者已经被删除"));
            return buildSimpleAuthenticationInfoForAdmin(dbAdmin, token.getPassword(), dbAdmin.getSalt(), dbAdmin.getPassword());
        } else if (token.getLoginType().equals(SysConst.loginType.SELLER.getCode())) {
            Seller dbSeller = sellerService.findByUsername(token.getUsername()).orElseThrow(() -> new AccountException("数据不存在或者已经被删除"));
            log.info("认证中---认证用户: {} ---认证信息: {}", dbSeller.getUsername(), token.getUsername(), SysConst.getLoginType(token.getLoginType()));
            return buildSimpleAuthenticationInfoForSeller(dbSeller, token.getPassword(), dbSeller.getSalt(), dbSeller.getPassword());
        } else if (token.getLoginType().equals(SysConst.loginType.APP.getCode())) {

        }

        return null;
    }

    /**
     * 构建管理员用户认证
     *
     * @param dbAdmin
     * @param password
     * @param salt
     * @param password1
     * @return
     */
    private AuthenticationInfo buildSimpleAuthenticationInfoForAdmin(Admin dbAdmin, char[] password, String salt, String password1) {
        String userName = dbAdmin.getUsername();
        String shiroLoginCountKey = CacheKeys.getShiroLoginCountKey(userName);
        String shiroIsLockKey = CacheKeys.getShiroIsLockKey(userName);
        String encryptPassword = UserUtils.getEncryptPassword(String.valueOf(password), salt);
        if (Objects.equals(encryptPassword, password1)) {
            Session session = SecurityUtils.getSubject().getSession();
            session.setAttribute(SysConst.SHIRO_USER_SESSION_NAME + System.currentTimeMillis(), dbAdmin);
            session.setTimeout(SysConst.SHIRO_SESSION_TIMEOUT);
            return new SimpleAuthenticationInfo(dbAdmin, password, getName());
        } else {
            increment(shiroLoginCountKey, shiroIsLockKey);
            throw new AccountException(SystemStatusCode.USER_NOT_PASSWORD.getMsg());
        }
    }

    /**
     * 构建商家用户认证
     *
     * @param dbSeller
     * @param password
     * @param salt
     * @param password1
     * @return
     */
    private AuthenticationInfo buildSimpleAuthenticationInfoForSeller(Seller dbSeller, char[] password, String salt, String password1) {
        String userName = dbSeller.getUsername();
        String shiroLoginCountKey = CacheKeys.getShiroLoginCountKey(userName);
        String shiroIsLockKey = CacheKeys.getShiroIsLockKey(userName);
        String encryptPassword = UserUtils.getEncryptPassword(String.valueOf(password), salt);
        if (Objects.equals(encryptPassword, password1)) {
            Session session = SecurityUtils.getSubject().getSession();
            session.setAttribute(SysConst.SHIRO_USER_SESSION_NAME + System.currentTimeMillis(), dbSeller);
            session.setTimeout(SysConst.SHIRO_SESSION_TIMEOUT);
            return new SimpleAuthenticationInfo(dbSeller, password, getName());
        } else {
            increment(shiroLoginCountKey, shiroIsLockKey);
            throw new AccountException(SystemStatusCode.USER_NOT_PASSWORD.getMsg());
        }
    }

    /**
     * 用户输入错误密码校验
     *
     * @param shiroLoginCountKey 错误次数key
     * @param shiroIsLockKey     锁定key
     */
    private void increment(String shiroLoginCountKey, String shiroIsLockKey) {
        stringRedisService.increment(shiroLoginCountKey, 1);
        stringRedisService.get(shiroLoginCountKey).ifPresent(shiroLoginCount -> {
            Integer firstErrorAccountErrorCount = accountProperties.getFirstErrorAccountErrorCount();
            Integer secondErrorAccountErrorCount = accountProperties.getSecondErrorAccountErrorCount();
            Integer thirdErrorAccountErrorCount = accountProperties.getThirdErrorAccountErrorCount();
            int parseInt = Integer.parseInt(shiroLoginCount);
            if (parseInt == firstErrorAccountErrorCount) {
                stringRedisService.expire(shiroIsLockKey, "LOCK", accountProperties.getFirstErrorAccountLockTime(), TimeUnit.MINUTES);
            } else if (parseInt == secondErrorAccountErrorCount) {
                stringRedisService.expire(shiroIsLockKey, "LOCK", accountProperties.getSecondErrorAccountLockTime(), TimeUnit.MINUTES);
            } else if (parseInt >= thirdErrorAccountErrorCount) {
                stringRedisService.expire(shiroIsLockKey, "LOCK", accountProperties.getThirdErrorAccountLockTime(), TimeUnit.HOURS);
            }
        });
    }
}
